package com.choudou5.solr.service;

import com.choudou5.solr.annotation.ServiceInfo;
import com.choudou5.solr.bean.SysDynamicBean;
import com.choudou5.solr.bean.common.*;
import com.choudou5.solr.framework.bean.BaseBean;
import com.choudou5.solr.framework.bean.UserBean;
import com.choudou5.solr.framework.bean.UserSessionBean;
import com.choudou5.solr.framework.constants.SysConsts;
import com.choudou5.solr.framework.constants.UserConsts;
import com.choudou5.solr.framework.holder.SessionContextHolder;
import com.choudou5.solr.framework.util.UserUtil;
import com.choudou5.solr.framework.util.cache.CacheHelper;
import com.choudou5.solr.framework.util.cache.EhCacheHelper;
import com.choudou5.solr.util.BeanMapper;
import com.choudou5.solr.util.ReflectionUtil;
import com.choudou5.solr.util.SysSeqUtil;
import com.choudou5.solr.util.cache.CacheFactory;
import com.choudou5.solr.util.cache.impl.LRUCache;
import com.choudou5.solr.util.exception.BizException;
import com.choudou5.solr.util.jdbc.DBUtil;
import com.choudou5.solr.util.jdbc.SqlFormatUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.*;

/**
 * Desc：基础接口
 * Author：choudou5
 * Date：2018-7-13
 */
public abstract class BaseService<B extends BaseBean> {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    private static final LRUCache<String, ServiceBean> cache = CacheFactory.newLRUCache(20);

    protected static final CacheHelper cacheHelper = EhCacheHelper.getInstance();

    /* 动态 */
    protected final SysDynamicService sysDynamicService = SysDynamicService.getInstance();
    protected void addSynDynamic(SysConsts.ActionType actionType, String subject, UserBean userBean) {
        sysDynamicService.add(new SysDynamicBean(actionType, subject, userBean));
    }
    protected void addSynDynamic(SysConsts.ActionType actionType, String subject, UserSessionBean userSessionBean) {
        sysDynamicService.add(new SysDynamicBean(actionType, subject, userSessionBean));
    }
    protected void addSynDynamic(SysConsts.ActionType actionType, String subject) {
        addSynDynamic(actionType, subject, SessionContextHolder.getUserSessionBean());
    }

    protected void addSynDynamic(SysConsts.ActionType actionType, String subject, String desc) {
        sysDynamicService.add(new SysDynamicBean(actionType, subject, desc, SessionContextHolder.getUserSessionBean()));
    }

    public void save(B b) throws BizException {
        if(b.getId() == null){
            add(b);
        }else{
            update(b);
        }
    }

    public void add(B b) throws BizException {
        if(b.getId() == null) {
            if(getBeanPkClasz(b) == Long.class)
                b.setId(SysSeqUtil.genTimeId());
            else
                b.setId(DBUtil.getMaxId(getTable(), 1));
        }

        if(b instanceof BaseUserBean){
            if(((BaseUserBean) b).getCreateBy() == null){
                Integer userId = UserUtil.getUserId();
                if(userId == null)
                    userId = UserConsts.SYS_USER_ID;
                ((BaseUserBean) b).setCreateBy(userId);
                ((BaseUserBean) b).setCreateTime(new Date());
            }
        }

        try {
            Map<String, Object> params = BeanMapper.map(b, HashMap.class);
            String sql = SqlFormatUtil.buildInsertSql(getTable(), params);
            DBUtil.insertData(sql);
        } catch (SQLException e) {
            logger.error(getTable()+" add fail", e);
            throw new BizException("添加"+getServiceDesc()+"数据失败");
        }
    }

    public void batchAdd(String sql) throws BizException {
        try {
            DBUtil.insertData(sql);
        } catch (SQLException e) {
            logger.error(getTable()+" batchAdd fail", e);
            throw new BizException("批量添加"+getServiceDesc()+"数据失败");
        }
    }

    public void update(B b) {
        update(b, false);
    }

    /**
     * 更新 非空字段
     * @param b
     */
    public void updateByNonNull(B b) {
        update(b, true);
    }

    private boolean update(B b, boolean ignoreNull) {
        try {
            Map<String, Object> params = BeanMapper.map(b, HashMap.class);
            String sql = SqlFormatUtil.buildUpdateByIdSql(getTable(), params, ignoreNull);
            int updateRow = DBUtil.updateData(sql);
            return updateRow>0?true:false;
        } catch (SQLException e) {
            logger.error(getTable()+" update fail", e);
            throw new BizException("修改"+getServiceDesc()+"数据失败");
        }
    }

    public boolean update(String sql) {
        try {
            int updateRow = DBUtil.updateData(sql);
            return updateRow>0?true:false;
        } catch (SQLException e) {
            logger.error(getTable()+" update fail", e);
            throw new BizException("修改"+getServiceDesc()+"数据失败");
        }
    }

    public void deleteById(Serializable id) {
        try {
            String sql = SqlFormatUtil.format("DELETE FROM ? WHERE ID = ?", getTable(), id);
            DBUtil.updateData(sql);
        } catch (SQLException e) {
            logger.error(getTable()+" delete fail", e);
            throw new BizException("删除"+getServiceDesc()+"数据失败");
        }
    }


    public B get(Serializable id) throws BizException {
        String sql = SqlFormatUtil.format("SELECT * FROM ? WHERE ID = ?", getTable(), id);
        try {
            return DBUtil.findOne(sql, getBeanClasz());
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }

    public B findOne(String where) throws BizException {
        String sql = "SELECT * FROM "+getTable()+" WHERE " + where + " LIMIT 1;";
        try {
            return DBUtil.findOne(sql, getBeanClasz());
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }

    public List<B> findList(String where) throws BizException {
        String sql = "SELECT * FROM "+getTable()+" WHERE " + where;
        try {
            return DBUtil.findList(sql, getBeanClasz());
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }

    public List<B> findList(SqlQueryBean sqlQueryBean) throws BizException {
        String sql = "SELECT * FROM "+getTable()+" WHERE " + sqlQueryBean.getWhere() + sqlQueryBean.getOrderSql()+ sqlQueryBean.toLimitScript();
        try {
            return DBUtil.findList(sql, getBeanClasz());
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }

    public List<B> findListAll(OrderBean orderBean) throws BizException {
        String sql = "SELECT * FROM "+getTable() + orderBean.getOrderSql();
        try {
            return DBUtil.findList(sql, getBeanClasz());
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"所有数据失败");
        }
    }

    public List<B> findListAll() throws BizException {
        String sql = "SELECT * FROM "+getTable();
        try {
            return DBUtil.findList(sql, getBeanClasz());
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"所有数据失败");
        }
    }

    public List<B> findListBySql(String sql) throws BizException {
        try {
            return DBUtil.findList(sql, getBeanClasz());
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }

    public List<Map<String, Object>> findListMap(String sql) throws BizException {
        try {
            return DBUtil.findListByParam(sql, null);
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }

    public <T extends Serializable> List<T> findOneColumnList(Class<T> clasz, String column, String where) throws BizException {
        String sql = "SELECT `"+column+"` FROM "+getTable()+" WHERE " + where;
        try {
            return DBUtil.findOneColumnList(sql, clasz);
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }

    public <T extends Serializable> List<T> findOneColumnListByAppendStatement(Class<T> clasz, String column, String appendStatement) throws BizException {
        String sql = "SELECT "+column+" FROM "+getTable() + appendStatement;
        try {
            return DBUtil.findOneColumnList(sql, clasz);
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }


    public String findOneColumn(String column, String where) throws BizException {
        String sql = "SELECT `"+column+"` FROM "+getTable()+" WHERE " + where;
        try {
            Object val =  DBUtil.findOneColumn(sql);
            return val != null ? val.toString() : null;
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数据失败");
        }
    }


    public PageResult<B> findPage(SqlQueryBean sqlQueryBean) throws BizException {
        PageBean pageBean = sqlQueryBean.getPageBean();
        PageResult<B> pageResult = new PageResult<>(pageBean);
        long count = count(sqlQueryBean.getWhere());
        pageResult.setTotalCount(count);
        if(count > 0 && count > pageBean.getStart()){
            pageResult.setResult(findList(sqlQueryBean));
        }
        return pageResult;
    }

    public DataTableBean<B> findDataTablePage(SqlQueryBean sqlQueryBean) throws BizException {
        PageBean pageBean = sqlQueryBean.getPageBean();
        DataTableBean tableBean = new DataTableBean(pageBean.getPageNo());
        long count = count(sqlQueryBean.getWhere());
        tableBean.setRecordsFiltered(count);
        tableBean.setRecordsTotal(count);
        if(count > 0 && count > pageBean.getStart()){
            tableBean.setData(findList(sqlQueryBean));
        }else{
            tableBean.setData(new ArrayList());
        }
        return tableBean;
    }


    public long count(String where) {
        try {
            return DBUtil.findCount(getTable(), where);
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数量失败");
        }
    }

    public long countAll() {
        try {
            return DBUtil.findAllCount(getTable());
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数量失败");
        }
    }

    public long countBySql(String sql) throws BizException {
        try {
            return DBUtil.findCountBySql(sql);
        } catch (SQLException e) {
            throw new BizException("查询"+getServiceDesc()+"数量失败");
        }
    }


    private Class<B> getBeanClasz() {
        return ReflectionUtil.getClassGenricType(this.getClass(), 0);
    }

    private Class getBeanPkClasz(B b) {
        Class clasz = ReflectionUtil.getClassGenricType(b.getClass(), 0);
        return clasz;
    }

    protected String getTable() {
        return "`"+getServiceBean().getTable()+"`";
    }

    protected String getServiceDesc() {
        return getServiceBean().getDesc();
    }

    private ServiceBean getServiceBean() {
        String cacheKey = getClass().getSimpleName();
        ServiceBean serviceBean = cache.get(cacheKey);
        if(serviceBean == null){
            ServiceInfo ann = getClass().getAnnotation(ServiceInfo.class);
            if(ann != null){
                serviceBean = new ServiceBean(ann.table(), ann.desc());
                cache.put(cacheKey, serviceBean);
            }else {
                throw new BizException(cacheKey + "类，未设置 @ServiceInfo");
            }
        }
        return serviceBean;
    }

}
